#pragma once
#include<iostream>
#include<string>
#include<sys/select.h>
#include<memory>
#include <unistd.h>
#include "InetAddr.hpp"
#include "Log.hpp"
#include"Socket.hpp"

using namespace SocketModule;

//辅助数组的最大值
#define NUM sizeof(fd_set) * 8
const int gdefaultfd = -1;


//仅关心读文件描述符集
class SelectServer
{
public:
    SelectServer(int port)
    :_port(port)
    ,_isrunning(false)
    ,_listen_socket(std::make_unique<TcpSocket>())
    {}

    //初始化服务器
    void Init()
    {
        //创建并完成listentcp套接字的整个过程
        _listen_socket->BuildTcpSocketMethod(_port);

        //初始化辅助数组
        for(int i = 0;i < NUM;i++)
        {
            _fd_array[i] = gdefaultfd;
        }

        //将listen套接字文件描述符添加进辅助数组
        _fd_array[0] = _listen_socket->Fd();
    }


    // 我们不能让accept来阻塞检测新连接到来，而应该让select来负责进行就绪事件的检测
    // 用户告诉内核，你要帮我关心&rfds，读事件啊！！

    //int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
    void Select_dpa(fd_set * rfds)
    {
        //清零rfds
        FD_ZERO(rfds);
        struct timeval timeout = {10, 0};

        //跟新nfds并寻找合法的fd添加进辅助数组
        int maxfd = gdefaultfd;
        for(int i = 0;i < NUM;i++)
        {
            if(_fd_array[i] == gdefaultfd)
                continue;
            //找到了
            FD_SET(_fd_array[i], rfds);
            if(maxfd < _fd_array[i])
            {
                maxfd = _fd_array[i];
            }
        }
        
        //等待
        int n = ::select(maxfd + 1,rfds,nullptr,nullptr,&timeout);
        //等待结果分类
        switch(n)
        {
            case 0:
                std::cout << "time out" << std::endl;
                break;
            case -1:
                perror("select");
                break;
            default:
            // 有事件就绪了
            // rfds: 内核告诉用户，你关心的rfds中的fd，有哪些已经就绪了！！
                std::cout << "有事件就绪了..." << " timeout: " << timeout.tv_sec << ":" << timeout.tv_usec << std::endl;
                Dispather(*rfds);
                break;
        }
    }

    //服务器启动并进入循环
    void Startloop()
    {
        //创建读文件描述符集
        fd_set rfds;
        _isrunning = true;

        //进入循环
        while(_isrunning)
        {
            //利用Select进行等待，就绪后分派任务：
            //1.listen就绪获取链接
            //2.普通读取
            Select_dpa(&rfds);
        }

        //循环结束，状态终止
        _isrunning = false;
    }


    //获取连接
    void Accepter()
    {
        InetAddr client;
        // listensockfd就绪了！获取新连接不就好了吗？
        int newfd = _listen_socket->Accepter(&client); // 会不会被阻塞呢？不会！select已经告诉我，listensockfd已经就绪了！只执行"拷贝"
        if (newfd < 0)
            return;
        else
        {
            std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;
            // recv()?? 读事件是否就绪，我们并不清楚！newfd也托管给select，让select帮我进行关心新的sockfd上面的读事件就绪
            // 怎么把新的newfd托管给select?让select帮我去关心newfd上面的读事件呢？把newfd，添加到辅助数组即可！
            int pos = -1;
            for(int i = 0;i < NUM;i++)
            {
                if(_fd_array[i] == gdefaultfd)//该位置没有被使用
                {
                    pos = i;
                    break;
                }
            }
            if(pos == -1)
            {
                LOG(LogLevel::ERROR) << "服务期满载...";
                close(newfd);
            }
            else
            {
                _fd_array[pos] = newfd;
            }
        }
    }

    void Recver(int who)
    {
        // 合法的，就绪的，普通的fd
        // 这里的recv，对不对啊！不完善！必须得有协议！
        char buffer[1024];
        ssize_t n = ::recv(_fd_array[who],buffer,sizeof(buffer) - 1,0);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# :" << buffer << std::endl;
            //读完后回显给客户端
            std::string message("echo# :");
            message += buffer;
            ::send(_fd_array[who],message.c_str(),message.size(),0);//返回不完善，完整的读写需要协议，这里存在bug
        }
        else if(n ==0)
        {
            LOG(LogLevel::DEBUG) << "客户端推出...,socketfd: " << _fd_array[who] ;
            close(_fd_array[who]);
            _fd_array[who] = gdefaultfd;
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: " << _fd_array[who];
            close(_fd_array[who]);
            _fd_array[who] = gdefaultfd;
        }
    }
    //分配任务
    void Dispather(fd_set &rfds)
    {
        //寻找合法fd
        for(int i = 0; i < NUM;i++)
        {
            if(_fd_array[i] == gdefaultfd)
                continue;
            //fd合法，判断：
            if(_fd_array[i] == _listen_socket->Fd())
            {
                //fd在读文件描述符集里
                if(FD_ISSET(_fd_array[i],&rfds))
                {
                    //litenfd获取链接
                    Accepter();
                }
            }
            else 
            {
                if(FD_ISSET(_fd_array[i],&rfds))
                {
                    //普通读取
                    Recver(i);//IO
                }
            }
        }
    }
    ~SelectServer()
    {}
private:
    bool _isrunning;
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    int _fd_array[NUM]; // 辅助数组，保存需要关心的fd
};